#include "devinsight.h"

#include "ddes/dms/ss_common_attr.h"
#include "ddes/dms/ss_dms_recovery.h"
#include "access/xlog.h"

#define LW_FLAG_HAS_WAITERS ((uint64)1LU << 30 << 32)

#define LW_FLAG_RELEASE_OK ((uint64)1LU << 29 << 32)
#define LW_FLAG_LOCKED ((uint64)1LU << 28 << 32)
#define LW_VAL_EXCLUSIVE (((uint64)1LU << 24 << 32) + (1LU << 47) + (1LU << 39) + (1LU << 31) + (1LU << 23) + (1LU << 15) + (1LU << 7))
#define LW_VAL_SHARED 1
#define LW_LOCK_MASK ((uint64)((1LU << 25 << 32) - 1))


DVI_DEF_GDB_FUNCTION(dviLWLock, "解析打印LWLock相关内容，状态位等。")
const char* dviLWLock(LWLock* lock)
{
    if (lock == NULL) {
        return "wrong input, NULL.";
    }
    StringInfoData res;
    initStringInfo(&res);
    uint64 state = pg_atomic_read_u64(&lock->state);

    appendStringInfo(&res, "Tranche Id: %u\n", lock->tranche);
    appendStringInfo(&res, "Tranche Name: %s\n", GetLWLockIdentifier(PG_WAIT_LWLOCK, lock->tranche));
    appendStringInfo(&res, "State: %lu [%s]\n", state, bx64b((void*)&state));
    appendStringInfo(&res, "\t63b-LW_FLAG_HAS_WAITERS: %s\n", (state & LW_FLAG_HAS_WAITERS) ? "true" : "false");
    appendStringInfo(&res, "\t62b-LW_FLAG_RELEASE_OK: %s\n", (state & LW_FLAG_RELEASE_OK) ? "true" : "false");
    appendStringInfo(&res, "\t61b-LW_FLAG_LOCKED: %s\n", (state & LW_FLAG_LOCKED) ? "true" : "false");
    appendStringInfo(&res, "\t57 8:48:8b-LW_VAL_EXCLUSIVE: %s\n", (state & LW_VAL_EXCLUSIVE) ? "true" : "false");
    for (int i = 5; i >=0 ; i--) {
        uint8 partnum = (uint8)((state >> (i * 8)) & 0xff);
        appendStringInfo(&res, "\t%d-%db: %s [%s, %u]\n",
            (i * 8 + 8), (i * 8 + 1),
            bx8b((void*)&partnum),
            (partnum & 0x80) ? "EXCLUSIVE" : "SHARED",   // 最高一位表示是否被占了独锁
            (partnum & 0x7F)                             // 其他七位表示S锁数量
        );
    }

    dlist_mutable_iter iter;
    int i = 0;
    appendStringInfo(&res, "Waiters:\n");
    dlist_foreach_modify(iter, &lock->waiters) {
        PGPROC *proc = dlist_container(PGPROC, lwWaitLink, iter.cur);
        appendStringInfo(&res, "\t%d| tid:%lu  sessid:%lu  wait-mode:%s\n",
            i++,
            proc->pid,
            proc->sessionid,
            proc->lwWaitMode == LW_SHARED ? "LW_SHARED" : "LW_EXCLUSIVE"
        );
    }
    return (const char*)res.data;
}


DVI_DEF_SQL_FUNCTION(dvi_lwlock, "查看或加解某个lwlock锁。一参是指针，传错会挂库，二参是 0:放锁, 1:S锁, 2:X锁, NULL及其他:仅展示")
Datum dvi_lwlock(PG_FUNCTION_ARGS)
{
    char* pointer = TextDatumGetCString(PG_GETARG_TEXT_P(0));
    long ptr_val = strtol(pointer, NULL, 16);
    LWLock *lock = (LWLock *)ptr_val;
    int mode = PG_ARGISNULL(1) ? -1 : PG_GETARG_INT32(1);
    static bool raise_warning = true;

    if (raise_warning) {
        raise_warning = false;
        PG_RETURN_CSTRING("初次使用警告，一定要确认指针参数传的是对的，不然直接野指针，数据库就挂了, 甚至可能随机带走一个。");
    } else if (lock->tranche < 0 || lock->tranche > LWTRANCHE_NATIVE_TRANCHE_NUM) {
        PG_RETURN_CSTRING("貌似tranche id有点不对，不像有这把锁的样子。");
    } else if (LWLockHeldByMe(lock) && mode != 0) {
        ereport(INFO, (errmsg("早就持锁了，别搞.")));
        mode = -1;
    }

    if (mode == 2) {
        ereport(LOG, (errmsg("carrotflag: LWLockAcquire X.  lock detail\n%s", dviLWLock(lock))));
        LWLockAcquire(lock, LW_EXCLUSIVE);
        ereport(LOG, (errmsg("carrotflag: LWLockAcquire get X.  lock detail\n%s", dviLWLock(lock))));
        ereport(INFO, (errmsg("X锁，加锁成功。")));
    } else if (mode == 1) {
        ereport(LOG, (errmsg("carrotflag: LWLockAcquire S.  lock detail\n%s", dviLWLock(lock))));
        LWLockAcquire(lock, LW_SHARED);
        ereport(LOG, (errmsg("carrotflag: LWLockAcquire get S.  lock detail\n%s", dviLWLock(lock))));
        ereport(INFO, (errmsg("S锁，加锁成功。")));
    } else if (mode == 0) {
        ereport(LOG, (errmsg("carrotflag: LWLockRelease lock detail\n%s", dviLWLock(lock))));
        LWLockRelease(lock);
        ereport(LOG, (errmsg("carrotflag: LWLockRelease done, lock detail\n%s", dviLWLock(lock))));
        ereport(INFO, (errmsg("放锁成功。")));
    }

    PG_RETURN_CSTRING(dviLWLock(lock));
}


DVI_DEF_SQL_FUNCTION(dvi_lwlock_held_info, "查看当前所有backend的lwlock持锁情况。")
Datum dvi_lwlock_held_info(PG_FUNCTION_ARGS)
{
    ReturnSetInfo* rsinfo = (ReturnSetInfo*)fcinfo->resultinfo;
    const int result_attr_num = 6;
    int i = 0;
    MemoryContext oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
    TupleDesc tupdesc = CreateTemplateTupleDesc(result_attr_num, false);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "tid", INT8OID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "lwlock", CSTRINGOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "mode", CSTRINGOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "tranche", INT4OID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "tranche_name", CSTRINGOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "tag", CSTRINGOID, -1, 0);
    Tuplestorestate* tupstore = tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
                                                      false, u_sess->attr.attr_memory.work_mem);
    MemoryContextSwitchTo(oldcontext);

    char* mode_str[3] = {"LW_EXCLUSIVE", "LW_SHARED", "LW_WAIT_UNTIL_FREE"};
    char lwlock_ptr_buffer[20] = {'\0'};
    bool  nulls[6] = {false};
    Datum values[6] = {0};
    int nBackends = BackendStatusArray_size;
    for (int n = 0; n < nBackends; ++n) {
        volatile PgBackendStatus* beentry = t_thrd.shemem_ptr_cxt.BackendStatusArray + n;
        if (beentry->lw_held_num == NULL) {
            continue;
        }

        values[0] = beentry->st_procpid;
        i = 0;
        int* lw_held_num = beentry->lw_held_num;
        LWLockHandle* handles = (LWLockHandle*)(beentry->lw_held_locks);
        LWLock* lock = handles[i].lock;
        for (; i < *lw_held_num; lock = handles[++i].lock) {
            snprintf_s(lwlock_ptr_buffer, 20, 19, "%p", lock);
            values[1] = PointerGetDatum(lwlock_ptr_buffer);
            values[2] = PointerGetDatum(mode_str[handles[i].mode]);
            values[3] = lock->tranche;
            values[4] = PointerGetDatum(GetLWLockIdentifier(PG_WAIT_LWLOCK, lock->tranche));
            // char lwlock_tag_buffer[64] = {'\0'};
            // LWLockDecodeProtectinfo(lock, lwlock_tag_buffer, 64);
            // values[5] = PointerGetDatum(lwlock_tag_buffer);
            values[5] = PointerGetDatum("内核暂不支持");
            tuplestore_putvalues(tupstore, tupdesc, values, nulls);
        }
    }

    tuplestore_donestoring(tupstore);
    rsinfo->returnMode = SFRM_Materialize;
    rsinfo->setResult = tupstore;
    rsinfo->setDesc = tupdesc;
    return (Datum)0;
}

DVI_DEF_SQL_FUNCTION(dvi_log_ctrl_info, "查看当前实时构建流控相关信息。")
Datum dvi_log_ctrl_info(PG_FUNCTION_ARGS)
{
    ReturnSetInfo* rsinfo = (ReturnSetInfo*)fcinfo->resultinfo;
    const int result_attr_num = 11;
    int i = 0;
    MemoryContext oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
    TupleDesc tupdesc = CreateTemplateTupleDesc(result_attr_num, false);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "ins_id", INT4OID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "current_rto", INT8OID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "prev_reply_time", TIMESTAMPTZOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "prev_calculate_time", TIMESTAMPTZOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "reply_time", TIMESTAMPTZOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "period_total_build", INT8OID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "build_rate", INT8OID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "prev_build_ptr", TEXTOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "realtime_build_ptr", TEXTOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "current_insert_ptr", TEXTOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber)++i, "sleep_time", INT4OID, -1, 0);

    Tuplestorestate* tupstore = tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
                                                      false, u_sess->attr.attr_memory.work_mem);
    MemoryContextSwitchTo(oldcontext);

    if (g_instance.dms_cxt.SSRecoveryInfo.enableRealtimeBuildLogCtrl) {
        for (int instId = 0; instId < DMS_MAX_INSTANCES; instId++) {
            realtime_build_ctrl_t rtBuildCtrl = g_instance.dms_cxt.SSRecoveryInfo.rtBuildCtrl[instId];
            if (rtBuildCtrl.replyTime == 0) {
                continue;
            }
            bool  nulls[11] = {false};
            Datum values[11] = {0};
            char prevBuildPtr[MAXFNAMELEN];
            char realtimeBuildPtr[MAXFNAMELEN];
            char currentInsertPtr[MAXFNAMELEN];
            int errorno = 0;
            errorno = snprintf_s(prevBuildPtr, sizeof(prevBuildPtr), sizeof(prevBuildPtr) - 1, "%X/%X",
                                (uint32)(rtBuildCtrl.prevBuildPtr >> 32), (uint32)rtBuildCtrl.prevBuildPtr);
            securec_check_ss(errorno, "", "");
            errorno = snprintf_s(realtimeBuildPtr, sizeof(realtimeBuildPtr), sizeof(realtimeBuildPtr) - 1, "%X/%X",
                                (uint32)(rtBuildCtrl.realtimeBuildPtr >> 32), (uint32)rtBuildCtrl.realtimeBuildPtr);
            securec_check_ss(errorno, "", "");
            XLogRecPtr currentInsertLsn = GetXLogInsertEndRecPtr();
            errorno = snprintf_s(currentInsertPtr, sizeof(currentInsertPtr), sizeof(currentInsertPtr) - 1, "%X/%X",
                                (uint32)(currentInsertLsn >> 32), (uint32)currentInsertLsn);
            securec_check_ss(errorno, "", "");
            values[0] = Int32GetDatum(instId);
            values[1] = Int64GetDatum(rtBuildCtrl.currentRTO);
            values[2] = TimestampTzGetDatum(rtBuildCtrl.prevReplyTime);
            values[3] = TimestampTzGetDatum(rtBuildCtrl.prevCalculateTime);
            values[4] = TimestampTzGetDatum(rtBuildCtrl.replyTime);
            values[5] = Int64GetDatum(rtBuildCtrl.periodTotalBuild);
            values[6] = Int64GetDatum(rtBuildCtrl.buildRate);
            values[7] = CStringGetTextDatum(prevBuildPtr);
            values[8] = CStringGetTextDatum(realtimeBuildPtr);
            values[9] = CStringGetTextDatum(currentInsertPtr);
            values[10] = Int32GetDatum(rtBuildCtrl.sleepTime);
            tuplestore_putvalues(tupstore, tupdesc, values, nulls);
        }
    }

    tuplestore_donestoring(tupstore);
    rsinfo->returnMode = SFRM_Materialize;
    rsinfo->setResult = tupstore;
    rsinfo->setDesc = tupdesc;
    return (Datum)0;
}